﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "ViewManipulationActions.h"
#include "GbmpUiPlatformCommandRegister.h"
#include "GbmpUiPlatformCommandIds.h"
#include "IUiDocumentViewManager.h"
#include "IUiView.h"
#include "ICanvas.h"
#include "TiltBox3d.h"
#include "IModelView.h"
#include "GcmpCommandState.h"
#include "IViewClipRange.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    bool GetModelViewBox(const IModelView* pModelView, gcmp::TiltBox3d& modelViewBox,
        bool computeCropPlanes = true, bool ignoreBottomPlane = true)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
        // 视图参数
        Vector3d originView = pModelView->GetOrigin();
        const Vector3d dirView = pModelView->GetViewDirection();
        const Vector3d upView = pModelView->GetUpDirection();
        const Vector3d rightView = dirView.Cross(upView);

        // double dLeft(-Constants::MATH_INFINITY), dRight(Constants::MATH_INFINITY), dUp(Constants::MATH_INFINITY), dDown(-Constants::MATH_INFINITY);
        // box size 是 Constants::MATH_INFINITY 时，TiltBox::IsIntersect 的会有正确性问题，所以暂时改成 Constants::MAX_MODEL_SIZE * Constants::MAX_MODEL_SIZE
        const double maxBoxHalfSize = Constants::MAX_MODEL_SIZE * Constants::MAX_MODEL_SIZE;
        double dLeft, dRight, dUp, dDown;
        dLeft = dDown = -1 * maxBoxHalfSize;
        dRight = dUp = maxBoxHalfSize;
        if (computeCropPlanes && pModelView->IsCroppedByViewport())
        {
            IViewClipRange* pClipRange = IViewClipRange::GetViewClipRange(pModelView->GetDocument(), pModelView->GetViewClipRangeId());
            double leftOffset(0.0), rightOffset(0.0), upOffset(0.0), downOffset(0.0);
            if (pClipRange->GetRectViewClipRange(leftOffset, rightOffset, upOffset, downOffset))
            {
                dLeft = -leftOffset;
                dRight = rightOffset;
                dUp = upOffset;
                dDown = -downOffset;
            }
        }

        double deep(-1 * maxBoxHalfSize), cut(0.0);
        if (!pModelView->GetTopPlaneOffset(cut))
        {
            if (!pModelView->GetCutPlaneOffset(cut))
            {
                cut = maxBoxHalfSize;
            }
        }

        //剖面视图下， 因为是视图裁剪， 所以无需考虑深度
        if (pModelView->GetBottomPlaneOffset(deep))
        {
            if (!pModelView->GetDeepPlaneOffset(deep)
                && ignoreBottomPlane)
            {
                deep = -1 * maxBoxHalfSize;
            }
        }
        Vector3d ptLocalBoxMin;
        Vector3d ptLocalBoxMax;
        // 注意：平面视图和剖面视图offset设置相反，平面视图没有按从cutplane - > deepplane标准格式设置offset偏移，暂时隐含内部处理
        // 这里存在隐患，需要将来移除到产品端定义可视范围。
        ptLocalBoxMin.SetZ(cut >= deep ? -cut : cut);
        ptLocalBoxMax.SetZ(cut >= deep ? -deep : deep);
        ptLocalBoxMin.SetX(dLeft);
        ptLocalBoxMax.SetX(dRight);
        ptLocalBoxMin.SetY(dDown);
        ptLocalBoxMax.SetY(dUp);
        Box3d boxLocal(ptLocalBoxMin, ptLocalBoxMax);

        Coordinate3d viewBoxCoord(originView, rightView, upView, dirView);
        modelViewBox = TiltBox3d(viewBoxCoord, boxLocal);

        return true;
    }
}

CmdZoomFit::CmdZoomFit(void)
    : CommandBase(ID_CMD_ZOOM_FIT)
{
}


CmdZoomFit::~CmdZoomFit(void)
{
}

gcmp::OwnerPtr<IAction> CmdZoomFit::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    ZoomFit();
    return nullptr;
}

void CmdZoomFit::ZoomFit() const
{    
    IUiDocumentViewManager* pUiDocViewMgr = IUiDocumentViewManager::Get();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pUiDocViewMgr, L"pUiDocViewMgr为空",L"GDMPLab",L"2024-03-30");
    IUiView* pCurrentUiView = pUiDocViewMgr->GetCurrentUiView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentUiView, L"pCurrentUiView为空",L"GDMPLab",L"2024-03-30");
    IModelView* pModelView = pCurrentUiView->GetModelView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    ICanvas* pCanvas = pCurrentUiView->GetCanvas();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCanvas, L"pCanvas为空",L"GDMPLab",L"2024-03-30");

    if (pModelView->IsCroppedByViewport())
    {
        TiltBox3d viewTiltBox;
        GetModelViewBox(pModelView, viewTiltBox);
        Box3d viewBox = viewTiltBox.BoundingBox();
        pCanvas->ZoomToBox(GetZoomBox(viewBox));
    }
    else
    {
        pCanvas->ZoomAll();
    }
}

gcmp::Box3d CmdZoomFit::GetZoomBox(const Box3d& box) const
{
    if (box.GetSize().Length() < Constants::MATH_INFINITY)
    {
        return box;
    }

    Vector3d minPtNew;
    {
        Vector3d minPtOld = box.GetMinPoint();
        if (Constants::MATH_NEGINFINITY < minPtOld.GetX() && minPtOld.GetX() < Constants::MATH_INFINITY)
        {
            minPtNew.SetX(minPtOld.GetX());
        }
        else
        {
            minPtNew.SetX(0.0);
        }
        if (Constants::MATH_NEGINFINITY < minPtOld.GetY() && minPtOld.GetY() < Constants::MATH_INFINITY)
        {
            minPtNew.SetY(minPtOld.GetY());
        }
        else
        {
            minPtNew.SetY(0.0);
        }
        if (Constants::MATH_NEGINFINITY < minPtOld.GetZ() && minPtOld.GetZ() < Constants::MATH_INFINITY)
        {
            minPtNew.SetZ(minPtOld.GetZ());
        }
        else
        {
            minPtNew.SetZ(0.0);
        }
    }

    Vector3d maxPtNew;
    {
        Vector3d maxPtOld = box.GetMaxPoint();
        if (Constants::MATH_NEGINFINITY < maxPtOld.GetX() && maxPtOld.GetX() < Constants::MATH_INFINITY)
        {
            maxPtNew.SetX(maxPtOld.GetX());
        }
        else
        {
            maxPtNew.SetX(0.0);
        }
        if (Constants::MATH_NEGINFINITY < maxPtOld.GetY() && maxPtOld.GetY() < Constants::MATH_INFINITY)
        {
            maxPtNew.SetY(maxPtOld.GetY());
        }
        else
        {
            maxPtNew.SetY(0.0);
        }
        if (Constants::MATH_NEGINFINITY < maxPtOld.GetZ() && maxPtOld.GetZ() < Constants::MATH_INFINITY)
        {
            maxPtNew.SetZ(maxPtOld.GetZ());
        }
        else
        {
            maxPtNew.SetZ(0.0);
        }
    }

    return Box3d(minPtNew, maxPtNew);
}

bool CmdZoomFit::IsVisible() const
{
    return GcmpCommandState::IsInFamilyEnvironment() ||
        GcmpCommandState::IsInProjectEnvironment() ||
        GcmpCommandState::IsInAnimationEnvironment();
}

bool CmdZoomFit::IsEnabled() const
{
    return GcmpCommandState::IsInFamilyEnvironment() ||
        GcmpCommandState::IsInProjectEnvironment() ||
        GcmpCommandState::IsInAnimationEnvironment();
}

REGISTER_GM_COMMAND(CmdZoomFit);


